/*!
 * jQuery UI Menubar @VERSION
 * http://jqueryui.com
 *
 * Copyright 2013 jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 * http://api.jqueryui.com/menubar/
 *
 * Depends:
 *  jquery.ui.core.js
 *  jquery.ui.widget.js
 *  jquery.ui.position.js
 *  jquery.ui.menu.js
 */
(function( $ ) {

$.widget( "ui.menubar", {
  version: "@VERSION",
  options: {
    items: "li",
    menus: "ul",
    icons: {
      dropdown: "ui-icon-triangle-1-s"
    },
    position: {
      my: "left top",
      at: "left bottom"
    },

    // callbacks
    select: null
  },

  _create: function() {
    // Top-level elements containing the submenu-triggering elem
    this.menuItems = this.element.children( this.options.items );

    // Links or buttons in menuItems, triggers of the submenus
    this.items = this.menuItems.children( "button, a" );

    // Keep track of open submenus
    this.openSubmenus = 0;

    this._initializeWidget();
    this._initializeMenuItems();
    this._initializeItems();
  },

  _initializeWidget: function() {
    this.element
      .addClass( "ui-menubar ui-widget-header ui-helper-clearfix" )
      .attr( "role", "menubar" );
    this._on({
      keydown: function( event ) {
        var active;

        // If we are in a nested sub-sub-menu and we see an ESCAPE
        // we must close recursively.
        if ( event.keyCode === $.ui.keyCode.ESCAPE &&
            this.active &&
            this.active.menu( "collapse", event ) !== true ) {
          active = this.active;
          this.active.blur();
          this._close( event );
          $( event.target ).blur().mouseleave();
          active.prev().focus();
        }
      },
      focusin: function() {
        clearTimeout( this.closeTimer );
      },
      focusout: function( event ) {
        this.closeTimer = this._delay( function() {
          this._close( event );
          this.items.attr( "tabIndex", -1 );
          this.lastFocused.attr( "tabIndex", 0 );
        }, 150 );
      },
      "mouseenter .ui-menubar-item": function() {
        clearTimeout( this.closeTimer );
      }
    } );
  },

  _initializeMenuItems: function() {
    var subMenus,
      menubar = this;

    this.menuItems
      .addClass( "ui-menubar-item" )
      .attr( "role", "presentation" )
      // TODO why do these not work when moved to CSS?
      .css({
        "border-width": "1px",
        "border-style": "hidden"
      });

    subMenus = this.menuItems.children( menubar.options.menus ).menu({
      position: {
        within: this.options.position.within
      },
      select: function( event, ui ) {
        // TODO don't hardcode markup selectors
        ui.item.parents( "ul.ui-menu:last" ).hide();
        menubar._close();
        ui.item.parents( ".ui-menubar-item" ).children().first().focus();
        menubar._trigger( "select", event, ui );
      },
      menus: this.options.menus
    })
      .hide()
      .attr({
        "aria-hidden": "true",
        "aria-expanded": "false"
      });

    this._on( subMenus, {
      keydown: function( event ) {
        // TODO why is this needed?
        $( event.target ).attr( "tabIndex", 0 );
        var parentButton,
          menu = $( this );
        // TODO why are there keydown events on a hidden menu?
        if ( menu.is( ":hidden" ) ) {
          return;
        }
        switch ( event.keyCode ) {
        case $.ui.keyCode.LEFT:
          // TODO why can't this call menubar.previous()?
          parentButton = menubar.active.prev( ".ui-button" );

          if ( this.openSubmenus ) {
            this.openSubmenus--;
          } else if ( this._hasSubMenu( parentButton.parent().prev() ) ) {
            menubar.active.blur();
            menubar._open( event, parentButton.parent().prev().find( ".ui-menu" ) );
          } else {
            parentButton.parent().prev().find( ".ui-button" ).focus();
            menubar._close( event );
            this.open = true;
          }

          event.preventDefault();
          // TODO same as above where it's set to 0
          $( event.target ).attr( "tabIndex", -1 );
          break;
        case $.ui.keyCode.RIGHT:
          this.next( event );
          event.preventDefault();
          break;
        }
      },
      focusout: function( event ) {
        // TODO why does this have to use event.target? Is that different from currentTarget?
        $( event.target ).removeClass( "ui-state-focus" );
      }
    });

    this.menuItems.each(function( index, menuItem ) {
      menubar._identifyMenuItemsNeighbors( $( menuItem ), menubar, index );
    });

  },

  _hasSubMenu: function( menuItem ) {
    return $( menuItem ).children( this.options.menus ).length > 0;
  },

  // TODO get rid of these - currently still in use in _move
  _identifyMenuItemsNeighbors: function( menuItem, menubar, index ) {
    var collectionLength = this.menuItems.length,
      isFirstElement = ( index === 0 ),
      isLastElement = ( index === ( collectionLength - 1 ) );

    if ( isFirstElement ) {
      menuItem.data( "prevMenuItem", $( this.menuItems[collectionLength - 1]) );
      menuItem.data( "nextMenuItem", $( this.menuItems[index+1]) );
    } else if ( isLastElement ) {
      menuItem.data( "nextMenuItem", $( this.menuItems[0]) );
      menuItem.data( "prevMenuItem", $( this.menuItems[index-1]) );
    } else {
      menuItem.data( "nextMenuItem", $( this.menuItems[index+1]) );
      menuItem.data( "prevMenuItem", $( this.menuItems[index-1]) );
    }
  },

  _initializeItems: function() {
    var menubar = this;

    this._focusable( this.items );
    this._hoverable( this.items );

    // let only the first item receive focus
    this.items.slice(1).attr( "tabIndex", -1 );

    this.items.each(function( index, item ) {
      menubar._initializeItem( $( item ), menubar );
    });
  },

  _initializeItem: function( anItem ) {
    var menuItemHasSubMenu = this._hasSubMenu( anItem.parent() );

    anItem
      .addClass( "ui-button ui-widget ui-button-text-only ui-menubar-link" )
      .attr( "role", "menuitem" )
      .wrapInner( "<span class='ui-button-text'></span>" );

    this._on( anItem, {
      focus:  function( event ){
        anItem.attr( "tabIndex", 0 );
        anItem.addClass( "ui-state-focus" );
        event.preventDefault();
      },
      focusout:  function( event ){
        anItem.attr( "tabIndex", -1 );
        this.lastFocused = anItem;
        anItem.removeClass( "ui-state-focus" );
        event.preventDefault();
      }
    } );

    if ( menuItemHasSubMenu ) {
      this._on( anItem, {
        click: this._mouseBehaviorForMenuItemWithSubmenu,
        focus: this._mouseBehaviorForMenuItemWithSubmenu,
        mouseenter: this._mouseBehaviorForMenuItemWithSubmenu
      });

      this._on( anItem, {
        keydown: function( event ) {
          switch ( event.keyCode ) {
          case $.ui.keyCode.SPACE:
          case $.ui.keyCode.UP:
          case $.ui.keyCode.DOWN:
            this._open( event, $( event.target ).next() );
            event.preventDefault();
            break;
          case $.ui.keyCode.LEFT:
            this.previous( event );
            event.preventDefault();
            break;
          case $.ui.keyCode.RIGHT:
            this.next( event );
            event.preventDefault();
            break;
          case $.ui.keyCode.TAB:
            break;
          }
        }
      });

      anItem.attr( "aria-haspopup", "true" );
      if ( this.options.icons ) {
        anItem.append( "<span class='ui-button-icon-secondary ui-icon " + this.options.icons.dropdown + "'></span>" );
        anItem.removeClass( "ui-button-text-only" ).addClass( "ui-button-text-icon-secondary" );
      }
    } else {
      this._on( anItem, {
        click: function() {
          if ( this.active ) {
            this._close();
          } else {
            this.open = true;
            this.active = $( anItem ).parent();
          }
        },
        mouseenter: function() {
          if ( this.open ) {
            this.stashedOpenMenu = this.active;
            this._close();
          }
        },
        keydown: function( event ) {
          if ( event.keyCode === $.ui.keyCode.LEFT ) {
            this.previous( event );
            event.preventDefault();
          } else if ( event.keyCode === $.ui.keyCode.RIGHT ) {
            this.next( event );
            event.preventDefault();
          }
        }
      });
    }
  },

  // TODO silly name, too much complexity
  // TODO why is this used for three types of events?
  _mouseBehaviorForMenuItemWithSubmenu: function( event ) {
    var isClickingToCloseOpenMenu, menu;

    // ignore triggered focus event
    if ( event.type === "focus" && !event.originalEvent ) {
      return;
    }
    event.preventDefault();

    menu = $(event.target).parents( ".ui-menubar-item" ).children( this.options.menus );

    // If we have an open menu and we see a click on the menuItem
    // and the menu thereunder is the same as the active menu, close it.
    // Succinctly: toggle menu open / closed  on the menuItem
    isClickingToCloseOpenMenu = event.type === "click" &&
      menu.is( ":visible" ) &&
      this.active &&
      this.active[0] === menu[0];

    if ( isClickingToCloseOpenMenu ) {
      this._close();
      return;
    }
    if ( event.type === "mouseenter" ) {
      this.element.find( ":focus" ).focusout();
      if ( this.stashedOpenMenu ) {
        this._open( event, menu);
      }
      this.stashedOpenMenu = undefined;
    }
    // If we already opened a menu and then changed to be "over" another MenuItem ||
    // we clicked on a new menuItem (whether open or not) or if we auto expand (i.e.
    // we expand regardless of click if there is a submenu
    if ( ( this.open && event.type === "mouseenter" ) || event.type === "click" ) {
      clearTimeout( this.closeTimer );
      this._open( event, menu );
      // Stop propagation so that menuItem mouseenter doesn't fire.  If it does it
      // takes the "selected" status off off of the first element of the submenu.
      event.stopPropagation();
    }
  },

  _destroy : function() {
    this.menuItems
      .removeClass( "ui-menubar-item" )
      .removeAttr( "role" )
      .css({
        "border-width": "",
        "border-style": ""
      });

    this.element
      .removeClass( "ui-menubar ui-widget-header ui-helper-clearfix" )
      .removeAttr( "role" )
      .unbind( ".menubar" );

    this.items
      .unbind( ".menubar" )
      .removeClass( "ui-button ui-widget ui-button-text-only ui-menubar-link ui-state-default" )
      .removeAttr( "role" )
      .removeAttr( "aria-haspopup" )
      .children( ".ui-icon" ).remove();

    // TODO fix this
    if ( false ) {
      // Does not unwrap
      this.items.children( "span.ui-button-text" ).unwrap();
    } else {
      // Does "unwrap"
      this.items.children( "span.ui-button-text" ).each( function(){
        var item = $( this );
        item.parent().html( item.html() );
      });
    }

    this.element.find( ":ui-menu" )
      .menu( "destroy" )
      .show()
      .removeAttr( "aria-hidden" )
      .removeAttr( "aria-expanded" )
      .removeAttr( "tabindex" )
      .unbind( ".menubar" );
  },

  _collapseActiveMenu: function() {
    if ( !this.active.is( ":ui-menu" ) ) {
      return;
    }
    this.active
      .menu( "collapseAll" )
      .hide()
      .attr({
        "aria-hidden": "true",
        "aria-expanded": "false"
      })
      .closest( this.options.items ).removeClass( "ui-state-active" );
  },

  _close: function() {
    if ( !this.active ) {
      return;
    }

    this._collapseActiveMenu();

    this.active = null;
    this.open = false;
    this.openSubmenus = 0;
  },

  _open: function( event, menu ) {
    var menuItem = menu.closest( ".ui-menubar-item" );

    if ( this.active && this.active.length &&
        this._hasSubMenu( this.active.closest( this.options.items ) ) ) {
          this._collapseActiveMenu();
    }

    menuItem.addClass( "ui-state-active" );
    // workaround when clicking a non-menu item, then hovering a menu, then going back
    // this way afterwards its still possible to tab back to a menubar, even if its
    // the wrong item
    // see also "click menu-less item, hover in and out of item with menu" test in menubar_core
    if ( !this.lastFocused ) {
      this.lastFocused = menu.prev();
    }

    this.active = menu
      .show()
      .position( $.extend({
        of: menuItem
      }, this.options.position ) )
      .removeAttr( "aria-hidden" )
      .attr( "aria-expanded", "true" )
      .menu( "focus", event, menu.children( ".ui-menu-item" ).first()  )
      .focus();

    this.open = true;
  },

  next: function( event ) {
    function shouldOpenNestedSubMenu() {
      return this.active &&
        this._hasSubMenu( this.active.closest( this.options.items ) ) &&
        this.active.data( "uiMenu" ) &&
        this.active.data( "uiMenu" ).active &&
        this.active.data( "uiMenu" ).active.has( ".ui-menu" ).length;
    }

    if ( this.open ) {
      if ( shouldOpenNestedSubMenu.call( this ) ) {
        // Track number of open submenus and prevent moving to next menubar item
        this.openSubmenus++;
        return;
      }
    }
    this.openSubmenus = 0;
    this._move( "next", event );
  },

  previous: function( event ) {
    if ( this.open && this.openSubmenus ) {
      // Track number of open submenus and prevent moving to previous menubar item
      this.openSubmenus--;
      return;
    }
    this.openSubmenus = 0;
    this._move( "prev", event );
  },

  _move: function( direction, event ) {
    var closestMenuItem = $( event.target ).closest( ".ui-menubar-item" ),
      nextMenuItem = closestMenuItem.data( direction + "MenuItem" ),
      focusableTarget = nextMenuItem.find( ".ui-button" );

    if ( this.open ) {
      if ( this._hasSubMenu( nextMenuItem ) ) {
        this._open( event, nextMenuItem.children( ".ui-menu" ) );
      } else {
        this._collapseActiveMenu();
        nextMenuItem.find( ".ui-button" ).focus();
        this.open = true;
      }
    } else {
      closestMenuItem.find( ".ui-button" );
      focusableTarget.focus();
    }
  }

});

}( jQuery ));
